package com.xu.cloud.shorturl.service;

import com.xu.cloud.common.util.AssertUtil;
import com.xu.cloud.common.util.NumberConvertUtil;
import com.xu.cloud.common.util.ShardingUtil;
import com.xu.cloud.shorturl.constant.ShortUrlConstant;
import com.xu.cloud.shorturl.converter.UrlInfoDTOConverter;
import com.xu.cloud.shorturl.exception.ShorUrlExceptionEnum;
import com.xu.cloud.shorturl.factory.ShortUrlInfoDOFactory;
import com.xu.cloud.shorturl.pojo.domain.ShortUrlInfoDO;
import com.xu.cloud.shorturl.pojo.dto.UrlInfoDTO;
import com.xu.cloud.shorturl.pojo.mapper.ShortUrlInfoMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.style.StylerUtils;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author xuguan
 * @since 2024/3/5
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ShortUrlService {

    private final ShortUrlInfoDOFactory shortUrlInfoDOFactory;
    private final ShortUrlInfoMapper shortUrlInfoMapper;
    private final ThreadPoolExecutor threadPoolExecutor;

    @NonNull
    @Cacheable(cacheNames = ShortUrlConstant.SHORT_URL_INFO_CACHE_PREFIX, key = "#p0", unless = "#result == null")
    public String queryOriginUrlByShortUrl(String shortUrl) {
        final ShortUrlInfoDO shortUrlInfoDo = getByShortUrl(shortUrl);
        AssertUtil.assertNotNull(shortUrlInfoDo, ShorUrlExceptionEnum.ORIGIN_URL_NOT_EXIST.info().newException());

        return shortUrlInfoDo.getOriginUrl();
    }

    public String generateShortUrl(String originUrl) {
        log.info("生成短地址开始, originUrl={}", originUrl);

        final ShortUrlInfoDO shortUrlInfoDO = shortUrlInfoDOFactory.create(originUrl);
        final String shortUrl = shortUrlInfoDO.getShortUrl();
        log.info("短地址创建成功, id={}, shortUrl={}, originUrl={}, ", shortUrlInfoDO.getId(), shortUrl, shortUrlInfoDO.getOriginUrl());

        shortUrlInfoMapper.insert(shortUrlInfoDO);
        log.info("短地址信息插入数据库成功");

        log.info("生成短地址结束, shortUrl={}", shortUrl);
        return shortUrl;
    }

    @NonNull
    public List<UrlInfoDTO> batchGenerateShortUrl(List<String> originUrls) {
        final int size = originUrls.size();
        final List<UrlInfoDTO> result = new ArrayList<>(size);
        final List<CompletableFuture<List<ShortUrlInfoDO>>> futures = new ArrayList<>();

        final Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
        for (List<String> shardingOriginUrls : ShardingUtil.shardingByShardingSize(originUrls, 10)) {
            // 执行异步任务
            final CompletableFuture<List<ShortUrlInfoDO>> future = CompletableFuture.supplyAsync(() -> {
                        // 异步线程日志同步
                        MDC.setContextMap(copyOfContextMap);
                        return doBatchGenerateShortUrl(shardingOriginUrls);
                    }, threadPoolExecutor)
                    .whenComplete((ret, th) -> {
                        if (Objects.nonNull(th)) {
                            log.error("异步任务执行失败!!!", th);
                        }
                        MDC.clear();
                    });
            futures.add(future);
        }

        result.addAll(collectBatchShortUrlInfoFutures(futures));

        return result;
    }

    public ShortUrlInfoDO getByShortUrl(String shorUrl) {
        final long id = NumberConvertUtil.baseToNum(shorUrl, NumberConvertUtil.BASE_62);
        return shortUrlInfoMapper.selectById(id);
    }

    public List<ShortUrlInfoDO> doBatchGenerateShortUrl(List<String> originUrls) {
        log.info("批量生成短地址线程任务开始, originUrls={}", StylerUtils.style(originUrls));
        final List<ShortUrlInfoDO> shortUrlInfoDOs = shortUrlInfoDOFactory.batchCreate(originUrls);

        shortUrlInfoMapper.batchInsert(shortUrlInfoDOs);

        log.info("批量生成短地址线程任务结束");
        return shortUrlInfoDOs;
    }

    private List<UrlInfoDTO> collectBatchShortUrlInfoFutures(List<CompletableFuture<List<ShortUrlInfoDO>>> futures) {
        final List<UrlInfoDTO> result = new ArrayList<>();

        // 等待所有任务完成，并收集结果
        try {
            final CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
            allOf.thenRun(() -> {
                        for (CompletableFuture<List<ShortUrlInfoDO>> future : futures) {
                            final List<UrlInfoDTO> urlInfoDTOs = UrlInfoDTOConverter.convertShortUrlInfoDOList(future.join());
                            result.addAll(urlInfoDTOs);
                        }
                    })
                    .join();
        } catch (CompletionException e) {
            throw ShorUrlExceptionEnum.BATCH_TASK_EXECUTE_FAIL.info().catchExceptionWithErrLog("批量生成短地址批量任务执行异常", e);
        }

        // final CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
        // try {
        //     allOf.thenRun(() -> {
        //                 for (CompletableFuture<List<ShortUrlInfoDO>> future : futures) {
        //                     List<UrlInfoDTO> urlInfoDTOs;
        //                     try {
        //                         urlInfoDTOs = UrlInfoDTOConverter.convertShortUrlInfoDOList(future.get());
        //                     } catch (InterruptedException | ExecutionException e) {
        //                         throw ShorUrlExceptionEnum.BATCH_TASK_EXECUTE_FAIL.info().catchExceptionWithErrLog("批量生成短地址批量任务执行异常", e);
        //                     }
        //                     result.addAll(urlInfoDTOs);
        //                 }
        //             })
        //             .get();
        // } catch (InterruptedException | ExecutionException e) {
        //     throw ShorUrlExceptionEnum.BATCH_TASK_EXECUTE_FAIL.info().catchExceptionWithErrLog("批量生成短地址批量任务执行异常", e);
        // }

        return result;
    }
}
